﻿
using System;

namespace CCompiler {
    /*enum InterOp
    {
        Add = 0,
        Sub, Not, Div, Mul, Mod,
        And, Or, Less, LessOrEq,
        Equal, NotEqual, Greater, GreaterOrEq,
        If, Assign, Goto
    }*/

    public abstract class Operand {
        public abstract string GetAsmOperand(CodeGenerator codeGen);
    }

    public class LabelOperand : Operand {
        public string label;

        public LabelOperand(string label) {
            this.label = label;
        }

        public override string ToString() {
            return label;
        }

        public override string GetAsmOperand(CodeGenerator codeGen) {
            return label;
        }
    }

    /* represents an immediate operand */
    public class NumberOperand : Operand {
        public int value;

        public NumberOperand(int val) {
            value = val;
        }

        public override string ToString() {
            return value.ToString();
        }

        public override string GetAsmOperand(CodeGenerator codeGen) {
            return value.ToString();
        }
    }

    /* represents a variable operand */
    public class VariableOperand : Operand {
        protected string varId;
        protected SymbolTable symtab;

        /* loc: the location of the variable in the symbole table */
        public VariableOperand(string id, SymbolTable symtable) {
            varId = id;
            symtab = symtable;
        }

        public VariableSymbol symbol {
            get {
                return (VariableSymbol)symtab.Lookup(varId);
            }
        }

        public override string ToString() {
            return varId;
        }

        public override string GetAsmOperand(CodeGenerator codeGen) {
            string ret = "";
            VariableSymbol symbol = (VariableSymbol)symtab.Lookup(varId);
            if (symbol.type == Type.Integer)
                ret += "dword ";
            else
                ret += "byte ";

            if (symbol.kind == VariableKind.Local) {
                ret += ("ptr [ebp - " + (symbol.offset + symbol.size).ToString() + "]");
                //ret += ("[ebp-" + symtab[value].offset.ToString() + "]");
            } else {
                ret += ("ptr [ebp + " + (symbol.offset + 8).ToString() + "]");
            }

            return ret;
        }
    }

    /* represents an array element operand */
    public class ArrayOperand : VariableOperand {
        private Operand index;

        public ArrayOperand(string arrId, Operand index, SymbolTable symtable)
            : base(arrId, symtable) {
            this.index = index;
        }

        public override string ToString() {
            return varId + "[" + index + "]";
        }

        public override string GetAsmOperand(CodeGenerator codeGen) {
            VariableSymbol symbol = (VariableSymbol)symtab.Lookup(varId);
            /* load the address of the array into ecx */
            //codeGen.EmitAsmInstr("lea", "ecx", "[ebp - " + (symbol.offset + symbol.size).ToString() + "]");
            /* load the index into esi */
            //codeGen.EmitAsmInstr("mov", "esi", index.GetAsmOperand(codeGen));
            codeGen.LoadOperandToReg("esi", index);

            string ret = "";
            if (symbol.type == Type.Integer)
                ret += "dword ";
            else
                ret += "byte ";
            ret += ("ptr " + "[ebp + esi * " + symbol.elemSize + " - " + (symbol.offset + symbol.size).ToString() + "]");
            //ret += ("ptr " + "[ecx + esi * " + symbol.elemSize + "]");

            return ret;
        }
    }

    public class FunctionOperand : Operand {
        public string funcId;

        public FunctionOperand(string funcId) {
            this.funcId = funcId;
        }

        public override string ToString() {
            return funcId;
        }

        public override string GetAsmOperand(CodeGenerator codeGen) {
            return funcId;
        }
    }

    /* represnts a three-address code instruction */
    public class InterInstruction {
        public TokenCode operation;

        /* operands */
        public Operand operand1;
        public Operand operand2;
        public Operand operand3;

        /* op: intermediate instruction operation (opcode)
         * op{1-3}: operands */
        public InterInstruction(TokenCode op, Operand op1, Operand op2, Operand op3) {
            operation = op;
            operand1 = op1;
            operand2 = op2;
            operand3 = op3;
        }

        /* returns a string representation for the three-address inst. */
        public override string ToString() {
            /*string[] opStrings = 
            {
              "+", "-", "!", "/", "*", "%",
              "&", "|", "<", "<=", "==", "!=",
              ">", ">=", "if", "=", "goto"
            };*/

            switch (operation) {
                case TokenCode.Enter:
                    return "enter " + operand1;

                case TokenCode.Param:
                    return "param " + operand1;

                case TokenCode.Call:
                    return operand1 + " = " + "call " + operand2;

                case TokenCode.Return:
                    string ret = "return ";
                    if (operand1 != null)
                        ret += operand1;

                    return ret;

                case TokenCode.PrintInt:
                    return "printInt " + operand1;

                case TokenCode.PrintStr:
                    return "printStr " + operand1;

                case TokenCode.PrintChar:
                    return "printChar " + operand1;

                case TokenCode.ReadInt:
                    return "readInt " + operand1;

                case TokenCode.ReadStr:
                    return "readStr " + operand1;

                case TokenCode.IncStackPtr:
                    return "incStackPtr " + operand1;

                case TokenCode.DecStackPtr:
                    return "decStackPtr " + operand1;

                case TokenCode.If:
                    /* conditional jump */
                    return "if " + operand1 + " goto " + operand2;

                case TokenCode.Goto:
                    /* unconditional jump */
                    return "goto " + operand1;

                case TokenCode.Equal:
                    /* copy instruction (assignment) */
                    return operand1 + " = " + operand2;

                default:
                    string opStr;

                    switch (operation) {
                        case TokenCode.And: opStr = "&"; break;
                        case TokenCode.Or: opStr = "|"; break;
                        case TokenCode.EqualEqual: opStr = "=="; break;
                        case TokenCode.NotEq: opStr = "!="; break;
                        case TokenCode.LessOrEq: opStr = "<="; break;
                        case TokenCode.GreaterOrEq: opStr = ">="; break;
                        /* otherwise: one char tokens , the token code = char ascii*/
                        default: opStr = ((char)operation).ToString(); break;
                    }
                    /* arithmatic or logical operations */
                    if ((operation == TokenCode.SubOp || operation == TokenCode.Not) && operand3 == null)
                        /* unary operation */
                        return String.Format("{0} {1} {2} {3}", operand1, "=", opStr, operand2);
                    else
                        /* binary operation */
                        return String.Format("{0} {1} {2} {3} {4}", operand1, "=", operand2, opStr, operand3);
            }
        }
    }
}